package main

import (
	"encoding/binary"
	"fmt"
	"net"
	"time"
)

const (
	pakSizeTcp = 4096
)

type TcpClientEx struct {
	_connect          *net.TCPConn      // 连接
	OnDisconnect      func()            // 断开连接的事件
	OnMsgReceive      func(bmsg []byte) // 收到消息的事件
	Connected         bool              // 是否已经连接
	_readDeadDuration time.Duration     // 读取超时时间(默认一分钟)
}

// 创建新的 Client
func NewTcpClient() *TcpClientEx {
	client := TcpClientEx{}
	return &client
}

func AttachTcpClient(conn *net.TCPConn) *TcpClientEx {
	client := TcpClientEx{}
	client._connect = conn
	go client.receiveProc()
	return &client
}

func (this *TcpClientEx) Connect(ip string, port int) bool {
	// 生成 IP
	addr := net.TCPAddr{}
	addr.IP = net.ParseIP(ip)
	addr.Port = port
	if addr.IP == nil {
		panic("Invalid address")
		return false
	}

	conn, err := net.DialTCP("tcp", nil, &addr)
	if err != nil {
		return false
	}
	this._connect = conn
	this.Connected = true

	go this.receiveProc()

	return true
}
func (this *TcpClientEx) DisConnect() {
	this._connect.Close()
	this.Connected = false
	if this.OnDisconnect != nil {
		this.OnDisconnect()
	}
}

// 设置读取超时时间（默认 10 秒），指定时间内没有收到包就断开连接
func (this *TcpClientEx) SetReadDeadDuration(t time.Duration) {
	this._readDeadDuration = t
}

// 接收消息携程
func (this *TcpClientEx) receiveProc() {
	defer this.DisConnect()
	msgr := make([]byte, pakSizeTcp+2)
	rcount := 0

	for {
		// 默认超时时间 10 秒
		if this._readDeadDuration == 0 {
			this._readDeadDuration = time.Second * 10
		}

		this._connect.SetReadDeadline(time.Now().Add(this._readDeadDuration))
		count, err := this._connect.Read(msgr[rcount:])
		if err != nil {
			break
		}
		rcount += count

		if rcount < 2 {
			continue
		}

		size := binary.LittleEndian.Uint16(msgr[:2])

		if int(size) > rcount-2 {
			continue
		}

		msg := make([]byte, size)
		copy(msg, msgr[2:size+2])
		copy(msgr[:rcount-int(size)-2], msgr[int(size)+2:rcount])
		rcount -= int(size + 2)
		// 收到消息
		if this.OnMsgReceive != nil {
			this.OnMsgReceive(msg)
		}

	}

}

func (this *TcpClientEx) Send(msg []byte) bool {
	if len(msg) > pakSizeTcp {
		panic("消息包大小不能超过" + fmt.Sprint(pakSizeTcp))
	}

	head := make([]byte, 2)
	binary.LittleEndian.PutUint16(head, uint16(len(msg)))

	if _, err := this._connect.Write(head); err != nil {
		return false
	}
	if _, err := this._connect.Write(msg); err != nil {
		return false
	}

	return true

}

func (this *TcpClientEx) Addr() string {
	return this._connect.RemoteAddr().(*net.TCPAddr).IP.String()
}

func (this *TcpClientEx) Port() int {
	return this._connect.RemoteAddr().(*net.TCPAddr).Port
}
